<?php
namespace WeChatPay\Request;

use function WeChatPay\FilterParam;
use function WeChatPay\FuncReturn;

/**
 * 申请退款API 商户订单号
 * 当交易发生之后一年内，由于买家或者卖家的原因需要退款时，卖家可以通过退款接口将支付金额退还给买家，微信支付将在收到退款请求并且验证成功之后，将支付款按原路退还至买家账号上。
 * Class WeChatPayTradeOutTradeNoRefundRequest
 * @package WeChatPay\Request
 */
class WeChatPayTradeOutTradeNoRefundRequest extends AbstractBaseRequest
{
    protected $apiUri = "v3/refund/domestic/refunds";

    /**
     * 必填参数列表
     * @var array
     */
    protected $mustAddParams = ['out_trade_no', 'out_refund_no', 'amount'];

    /**
     * 属性映射方法名称
     * @var array
     */
    protected $propertyMapSetFunction = [
        'out_trade_no'      =>  'setOutTradeNo',
        'out_refund_no'     =>  'setOutRefundNo',
        'reason'            =>  'setReason',
        'funds_account'     =>  'setFundsAccount',
        'amount'            =>  'setAmount',
        'goods_detail'      =>  'setGoodsDetail'
    ];

    /**
     * 商户订单号
     * @var string
     */
    protected $out_trade_no = "";

    /**
     * 商户退款单号:商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔
     * @var string
     */
    protected $out_refund_no = "";

    /**
     * 退款原因:若商户传入，会在下发给用户的退款消息中体现退款原因
     * @var string
     */
    protected $reason = "";

    /**
     * 退款资金来源:若传递此参数则使用对应的资金账户退款，否则默认使用未结算资金退款（仅对老资金流商户适用）枚举值:AVAILABLE：可用余额账户
     * @var string
     */
    protected $funds_account = "";

    /**
     * 金额信息:订单金额信息
     * @var array
     */
    protected $amount = [
        //int|must|退款金额
        'refund'   =>   0,
        //list|退款出资账户及金额
        'from'     =>   [[
            //string|must|出资账户类型:退款需要从指定账户出资时，传递此参数指定出资金额（币种的最小单位，只能为整数）。
            //同时指定多个账户出资退款的使用场景需要满足以下条件：
            //  1、未开通退款支出分离产品功能；
            //  2、订单属于分账订单，且分账处于待分账或分账中状态。
            //参数传递需要满足条件：
            //  1、基本账户可用余额出资金额与基本账户不可用余额出资金额之和等于退款金额；
            //  2、账户类型不能重复。
            //上述任一条件不满足将返回错误
            'account'   =>  '',
            //int|must|对应账户出资金额
            'amount'    =>  0
        ]],
        //int|must|原订单金额:原支付交易的订单总金额，单位为分，只能为整数
        'total'     =>  0,
        //string|must|退款币种:符合ISO 4217标准的三位字母代码，目前只支持人民币：CNY
        'currency'  =>  'CNY'
    ];

    /**
     * 退款商品:指定商品退款需要传此参数，其他场景无需传递
     * @var array
     */
    protected $goods_detail = [[
        //string|must|商户侧商品编码:由半角的大小写字母、数字、中划线、下划线中的一种或几种组成
        'merchant_goods_id'     =>  '',
        //string|微信支付商品编码:微信支付定义的统一商品编号（没有可不传）
        'wechatpay_goods_id'    =>  '',
        //string|商品名称:商品的实际名称
        'goods_name'            =>  '',
        //int|must|商品单价:商品单价金额，单位为分
        'unit_price'            =>  0,
        //int|must|商品退款金额:商品退款金额，单位为分
        'refund_amount'         =>  0,
        //int|must|商品退货数量:单品的退款数量
        'refund_quantity'       =>  0
    ]];

    /**
     * 获取配置基础参数列表
     * @return array
     */
    public function getBaseParams(): array {
        return ['refund_notify_url'];
    }

    /**
     * 获取商户订单号
     * @return string
     */
    public function getOutTradeNo(): string
    {
        return $this->out_trade_no;
    }

    /**
     * 设置商户订单号
     * @param string $out_trade_no
     */
    public function setOutTradeNo(string $out_trade_no): void
    {
        $this->addBodyParams('out_trade_no');
        $this->out_trade_no = $out_trade_no;
    }

    /**
     * 获取商户退款单号
     * @return string
     */
    public function getOutRefundNo(): string
    {
        return $this->out_refund_no;
    }

    /**
     * 设置商户退款单号
     * @param string $out_refund_no
     */
    public function setOutRefundNo(string $out_refund_no): void
    {
        $this->addBodyParams('out_refund_no');
        $this->out_refund_no = $out_refund_no;
    }

    /**
     * 获取退款原因
     * @return string
     */
    public function getReason(): string
    {
        return $this->reason;
    }

    /**
     * 设置退款原因
     * @param string $reason
     */
    public function setReason(string $reason): void
    {
        $this->addBodyParams('reason');
        $this->reason = $reason;
    }

    /**
     * 获取退款资金来源
     * @return string
     */
    public function getFundsAccount(): string
    {
        return $this->funds_account;
    }

    /**
     * 设置退款资金来源
     * @param string $funds_account
     */
    public function setFundsAccount(string $funds_account): void
    {
        $this->addBodyParams('funds_account');
        $this->funds_account = $funds_account;
    }

    /**
     * 获取金额信息
     * @return array
     */
    public function getAmount(): array
    {
        return $this->amount;
    }

    /**
     * 设置金额信息
     * @param array $amount
     * @throws \Exception
     */
    public function setAmount(array $amount): void
    {
        $this->addBodyParams('amount');

        if (!array_key_exists('refund', $amount)) {
            throw new \Exception("amount.refund param is must");
        }

        if (!array_key_exists('total', $amount)) {
            throw new \Exception("amount.total param is must");
        }

        if (!array_key_exists('currency', $amount)) {
            throw new \Exception("amount.currency param is must");
        }

        $fromData = [];
        if (array_key_exists('from', $amount)) {
            if (!is_array($amount['from'])) {
                throw new \Exception("amount.from param must is list");
            }

            foreach ($amount['from'] as $key => $tempFormData) {
                if (!is_array($tempFormData)) {
                    throw new \Exception("amount.from.{$key} param must is array");
                }

                if (!array_key_exists('account', $tempFormData)) {
                    throw new \Exception("amount.from.{$key}.account param is must");
                }

                if (!array_key_exists('amount', $tempFormData)) {
                    throw new \Exception("amount.from.{$key}.amount param is must");
                }

                $fromData[$key] = [
                    'account'   =>  $tempFormData['account'],
                    'amount'   =>  $tempFormData['amount']
                ];
            }
        }

        $tempAmount = [
            'refund'        =>  $amount['refund'],
            'total'         =>  $amount['total'],
            'currency'      =>  $amount['currency'],
            'from'          =>  $fromData,
        ];

        $this->amount = FilterParam($tempAmount);
    }

    /**
     * 获取退款商品信息
     * @return array
     */
    public function getGoodsDetail(): array
    {
        return $this->goods_detail;
    }

    /**
     * 设置退款商品信息
     * @param array $goods_detail
     * @throws \Exception
     */
    public function setGoodsDetail(array $goods_detail): void
    {
        $this->addBodyParams('goods_detail');

        $tempGoodsDetail = [];
        foreach ($goods_detail as $key => $tempData) {
            if (!is_array($tempData)) {
                throw new \Exception("goods_detail.{$key} param must is array");
            }

            if (!array_key_exists('merchant_goods_id', $tempData)) {
                throw new \Exception("goods_detail.{$key}.merchant_goods_id param is must");
            }

            if (!array_key_exists('unit_price', $tempData)) {
                throw new \Exception("goods_detail.{$key}.unit_price param is must");
            }

            if (!array_key_exists('refund_amount', $tempData)) {
                throw new \Exception("goods_detail.{$key}.refund_amount param is must");
            }

            if (!array_key_exists('refund_quantity', $tempData)) {
                throw new \Exception("goods_detail.{$key}.refund_quantity param is must");
            }

            $tempGoodsDetail[$key] = [
                'merchant_goods_id'     =>  $tempData['merchant_goods_id'],
                'unit_price'     =>  $tempData['unit_price'],
                'refund_amount'     =>  $tempData['refund_amount'],
                'refund_quantity'     =>  $tempData['refund_quantity'],
                'wechatpay_goods_id'     =>  $tempData['wechatpay_goods_id'] ?? "",
                'goods_name'     =>  $tempData['goods_name'] ?? "",
            ];
        }

        $this->goods_detail = FilterParam($tempGoodsDetail);
    }
}